#include <hd_led.h>
#include <hd_oled.h>
#include <delay.h>
#include <hd_oled_protocol_i2c.h>
#include <hd_oled_protocol_spi.h>
#include <hd_motor.h>
#include <pd_i2c_hd.h>
#include "main.h"


#define ARRAY_SIZE(x)       sizeof(x) / sizeof(x[0])
#define USE_HARDWARE_SPI    1
#define USE_HARDWARE_I2C    1
OLED oled;

extern void SystemClock_Config(void);

void oled_instance(OLED *oled);

void Motor1_instance();

void Motor2_instance();

int main(void) {
    SystemClock_Config();

    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

    oled_instance(&oled);

    OLED_printf(&oled, 0, 0, "TIM PWM MOTOR");
    OLED_printf(&oled, 0, 16, "Speed:");
    OLED_refresh(&oled);

    Motor motor1, motor2;
    Motor1_instance(&motor1);
    Motor2_instance(&motor2);

    int8_t speed = 0;
    while (1) {
        speed += 20;
        speed = speed > 100 ? -100 : speed;
        OLED_printf(&oled, 48, 16, "%4d", speed);
        OLED_refresh(&oled);

        Motor_setSpeed(&motor1, speed);
        Motor_setSpeed(&motor2, speed);
        delay_ms(3000);
    }
}

void Motor1_instance(Motor *motor1) {
    // PWMA: PC6 -- AF2 -- TIM3_CH1
    static GPIO motor1GPIO[1];
    GPIO_constructor(motor1GPIO, GPIOC, LL_GPIO_PIN_6);
    motor1GPIO->Mode = LL_GPIO_MODE_ALTERNATE;
    motor1GPIO->Alternate = LL_GPIO_AF_2;
    static uint32_t motorChannel1[] = {LL_TIM_CHANNEL_CH1};
    static TimerCh motor1TimerChannel;
    TimerCh_constructor(&motor1TimerChannel, motor1GPIO, ARRAY_SIZE(motor1GPIO),
                        motorChannel1, ARRAY_SIZE(motorChannel1));

    // Timer Base Unit: freq = 1kHz, duty is variable, resolution = 1%
    static Timer motor1Timer;
    Timer_constructor(&motor1Timer, TIM3, 84 - 1, 100 - 1);
    static TimerOC motor1TimerOC;
    TimerOC_constructor(&motor1TimerOC, &motor1Timer, &motor1TimerChannel, 0);
    static TimerPWM motor1TimerPWM;
    TimerPWM_constructor(&motor1TimerPWM, &motor1Timer, &motor1TimerOC);
    TimerPWM_init(&motor1TimerPWM);

    // AIN1, AIN2
    static GPIO aIn1, aIn2;
    GPIO_constructor(&aIn1, GPIOE, LL_GPIO_PIN_3);
    GPIO_constructor(&aIn2, GPIOE, LL_GPIO_PIN_4);

    Motor_constructor(motor1, &motor1TimerPWM, &aIn1, &aIn2);
    Motor_init(motor1);
    Motor_start(motor1);
}

void Motor2_instance(Motor *motor2) {
    // PWMA: PC6 -- AF2 -- TIM3_CH1
    static GPIO motor2GPIO[1];
    GPIO_constructor(motor2GPIO, GPIOD, LL_GPIO_PIN_12);
    motor2GPIO->Mode = LL_GPIO_MODE_ALTERNATE;
    motor2GPIO->Alternate = LL_GPIO_AF_2;
    static uint32_t motorChannel1[] = {LL_TIM_CHANNEL_CH1};
    static TimerCh motor2TimerChannel;
    TimerCh_constructor(&motor2TimerChannel, motor2GPIO, ARRAY_SIZE(motor2GPIO),
                        motorChannel1, ARRAY_SIZE(motorChannel1));

    // Timer Base Unit: freq = 1kHz, duty is variable, resolution = 1%
    static Timer motor2Timer;
    Timer_constructor(&motor2Timer, TIM4, 84 - 1, 100 - 1);
    static TimerOC motor2TimerOC;
    TimerOC_constructor(&motor2TimerOC, &motor2Timer, &motor2TimerChannel, 0);
    static TimerPWM motor2TimerPWM;
    TimerPWM_constructor(&motor2TimerPWM, &motor2Timer, &motor2TimerOC);
    TimerPWM_init(&motor2TimerPWM);

    // AIN1, AIN2
    static GPIO bIn1, bIn2;
    GPIO_constructor(&bIn1, GPIOE, LL_GPIO_PIN_5);
    GPIO_constructor(&bIn2, GPIOE, LL_GPIO_PIN_6);
    Motor_constructor(motor2, &motor2TimerPWM, &bIn1, &bIn2);
    Motor_init(motor2);
    Motor_start(motor2);
}

void oled_instance(OLED *oled) {
    // I2C gpio init
    static GPIO oledSckGPIO, oledSdaGPIO;
    GPIO_constructor(&oledSckGPIO, GPIOB, LL_GPIO_PIN_10); // I2Cx_SCK
    GPIO_constructor(&oledSdaGPIO, GPIOB, LL_GPIO_PIN_11); // I2Cx_SDA

#ifdef USE_HARDWARE_I2C
    static I2CHardware i2c2;
    I2CHardware_constructor(&i2c2, I2C2, &oledSckGPIO, &oledSdaGPIO);
#elif USE_SIMULATE_I2C
    static I2CSimulate i2c2;
    I2CSimulate_constructor(&i2c2, &oledSckGPIO, &oledSdaGPIO);
#endif
    I2C_init((I2C *)&i2c2);

    // OLED protocol of I2C
    static OLEDProtocolI2C oledProtocolI2c;
    OLEDProtocolI2C_constructor(&oledProtocolI2c, (I2C *)&i2c2);

    // OLED font instance.
    static OLEDFont oledFont;
    OLEDFont_contructor(&oledFont, NULL);

    // OLED instance of 4-PIN(I2C)
    OLED_constructor(oled, (OLEDProtocol *)&oledProtocolI2c, &oledFont);

    OLED_init(oled);

    OLED_clear(oled);
    OLED_refresh(oled);
}
